//add stephen ++
/*
Sample：样本长度，音频数据最基本的单位，常见的有 8 位和 16 位；
Channel：声道数，分为单声道 mono 和立体声 stereo；
Frame：帧，构成一个完整的声音单元，所谓的声音单元是指一个采样样本，Frame = Sample * channel；
Rate：又称 sample rate，采样率，即每秒的采样次数，针对帧而言；
Period Size：周期，每次硬件中断处理音频数据的帧数，对于音频设备的数据读写，以此为单位；
  Buffer Size：数据缓冲区大小，这里指 runtime 的 buffer size，而不是结构图 snd_pcm_hardware 中定义的 buffer_bytes_max;
  一般来说 buffer_size = period_size * period_count， period_count 相当于处理完一个 buffer 数据所需的硬件中断次数。 
  read/write pointer
               |
               v
+-----------------------------+--------------+--------------+
|              |              |              |              |buffer = 4 periods
+--------------+--------------+--------------+--------------+
                     ^
                     |
               +---+---+------+
               |   |   |  ... |period = 1024 frames
               +---+---+------+
                     ^
                     |
                   +---+
                   |L|R|frame = 2 samples (left + right)
                   +---+

                        sample = 2 bytes (16bit)
						
root@jb1505:/data # tinyplay fly-2ch-8k-125s.wav  -D 0 -d 2 -p 160 -n 2
size:1280 bytes
Playing sample: 2 ch, 8000 hz, 16 bit
Channels = 2，rate = 8000hz,period_size = 160,period_count = 2 求buffer size大小
Size = period_size * period_count = 160帧 * 2 = 320帧= 320 * 2 samples (left + right) = 320 * 2 * 16 bit =  1280B
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));

PCM码流需要满足：8K，16bit，单声道，20毫秒（320字节）或者60毫秒（960字节）发一次

8k-->8000帧 ----》8000 *2 sample = 16000 sample = 16000 * 16 bit = 256000 bit =  256000/8 byte = 32000 byte
1000 ms   ---> 32000 byte
1ms ---> 32byte
20ms  ---> 640byte 这里比协议手册要求的20ms 320少一半，应该是只算了一个通道，可能需要做转换
这里的单声道应该是左右声道都是相同的数据
*/
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <android/log.h>


#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <semaphore.h>
#include <pthread.h>
#include <jni.h>
#include "net_audio_play.h"


#define LOG_TAG "TT_AUDIO"
#define LOGD(...)	__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...)	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)


#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT  0x20746d66
#define ID_DATA 0x61746164

struct riff_wave_header {
    uint32_t riff_id;
    uint32_t riff_sz;
    uint32_t wave_id;
};

struct chunk_header {
    uint32_t id;
    uint32_t sz;
};

struct chunk_fmt {
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bits_per_sample;
};

static int close_play = 0;
static int capturing = 1;

#define FORMAT_PCM 1

struct wav_header {
    uint32_t riff_id;
    uint32_t riff_sz;
    uint32_t riff_fmt;
    uint32_t fmt_id;
    uint32_t fmt_sz;
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bits_per_sample;
    uint32_t data_id;
    uint32_t data_sz;
};




#define pcm_card 	0
#define pcm_device	2 //0
#define audio_channels	2
#define audio_rate	8000
#define audio_period_size 160  //320 
#define audio_period_count	2

static struct pcm *pcm_play; //上位机获取的pcm数据,发送给天通的数据pcm接口
static struct pcm *pcm_record; //从天通获取的pcm数据发送给上位机的数据pcm接口
static char *send_to_tt_buffer;	//上位机获取的pcm数据,发送给天通的数据buffer
static int send_to_tt_size;
//pcm音频数据有符号数据
static char *recieve_from_tt_buffer;	//从天通获取的pcm数据发送给上位机的数据buffer
static int recieve_from_tt_size;

static struct pcm_config pcm_config_audio = {
    .channels = audio_channels,//2
    .rate = audio_rate,
    .period_size = audio_period_size,//160
    .period_count = audio_period_count,//2
    .format = PCM_FORMAT_S16_LE,
};
/*
struct loop_ctl_t
{   
    pthread_t thread;
    sem_t   sem;
    bool is_exit;
    pthread_mutex_t lock;
    void *dev;
    bool state;
    struct pcm *pcm;
    struct ring_buffer *ring_buf;
};

struct dsp_loop_t {
    struct loop_ctl_t in;
    struct loop_ctl_t out;
    struct loop_ctl_t in_codec;
    struct loop_ctl_t out_codec;
    bool state;
};
*/

int audio_stop(void);

static void stream_close(int sig)
{
    /* allow the stream to be closed gracefully */
    signal(sig, SIG_IGN);
    close_play = 1;
	capturing = 0;
	
}

static void sigint_handler(int sig)
{
    capturing = 0;
}


/*
声卡除初始化
-1:初始化错误
0:初始化成功
*/
int audio_init(void)
{
	audio_stop();
    LOGD("audio_init  start\n");
#if 1 //打开天通play 播放声卡 数据流:  上位机pc --->android lc1860 ---> 天通
	pcm_play = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
	if(!pcm_play || !pcm_is_ready(pcm_play)){
		//fprintf(stderr,"Unable to open PCM play device (%s)\n",
		//		pcm_get_error(pcm_play));
        LOGD("Unable to open PCM play device (%s)\n",
				pcm_get_error(pcm_play));	
		
		return -1;
	}
	send_to_tt_size = pcm_frames_to_bytes(pcm_play, pcm_get_buffer_size(pcm_play));
	send_to_tt_buffer = malloc(send_to_tt_size);
	if(!send_to_tt_buffer){
		//fprintf(stderr, "Unable to allocate %d bytes\n",send_to_tt_size);
        LOGD("Unable to allocate %d bytes\n",send_to_tt_size);
		free(send_to_tt_buffer);
		pcm_close(pcm_play);
		return -1;
	}
    LOGD("audio_init  start ok\n");
#endif

    //打开天通record 录音接口，数据流天通语音 ----> android lc1860 ---> 上位机pc
	pcm_record = pcm_open(pcm_card, pcm_device, PCM_IN |PCM_MMAP | PCM_NOIRQ|PCM_MONOTONIC, &pcm_config_audio);
	if(!pcm_record || !pcm_is_ready(pcm_record)){
		//fprintf(stderr,"Unable to open PCM record device (%s)\n",
		//		pcm_get_error(pcm_record));
		LOGI("Unable to open PCM record device (%s)\n",
				pcm_get_error(pcm_record));
		return -1;
	}
	recieve_from_tt_size = pcm_frames_to_bytes(pcm_record, pcm_get_buffer_size(pcm_record));
	recieve_from_tt_buffer = malloc(recieve_from_tt_size);
	if(!recieve_from_tt_buffer){
		//fprintf(stderr,"Unable to allocate %d bytes\n",recieve_from_tt_size);
		LOGI("Unable to allocate %d bytes\n",recieve_from_tt_size);
		free(recieve_from_tt_buffer);
		pcm_close(pcm_record);
		return -1;
	}

    LOGD("audio_init ok send_to_tt_size:%d,recieve_from_tt_size:%d\n",send_to_tt_size,recieve_from_tt_size);
	return 0;
}

int audio_stop(void)
{
	if(pcm_play){
		pcm_close(pcm_play);
		pcm_play = NULL;
	}
	if(pcm_record){
		pcm_close(pcm_record);
		pcm_record = NULL;
	}
	
	if(send_to_tt_buffer){
		free(send_to_tt_buffer);
		send_to_tt_buffer = NULL;
	}
	if(recieve_from_tt_buffer){
		free(recieve_from_tt_buffer);
		recieve_from_tt_buffer = NULL;
	}
	
	//printf(" audio stop \n");
	LOGI(" audio stop \n");
	return 0;
	
}
/*
获取上位机的pcm数据,发送给天通
返回0:数据成功写完
返回非0：异常
 char *buf：上位机pcm buf
 int size:pcm数据大小
 数据流:上位机pc --->android lc1860 ---> 天通
*/
int send_audio_to_tt(char *buf, int size){
	int ret,write_sample;
	int to_write = size;
	char *tmp = NULL;
	tmp = buf;
    LOGI(" send_audio_to_tt start \n");
    if(!pcm_play || !pcm_is_ready(pcm_play)){
        LOGI(" send_audio_to_tt err  pcm_play not open\n");
        return -2;
    }
    //这里要判断是否完成了声卡初始化就绪,否则返回错误
	while((to_write > 0) && !close_play){
		write_sample = (to_write < send_to_tt_size) ? to_write : send_to_tt_size;
		ret = pcm_mmap_write(pcm_play, tmp, write_sample);
		if(ret){//写pcm声卡报错,重新打开pcm声卡
			//fprintf(stderr,"Error play sample :%s \n", pcm_get_error(pcm_play));
			LOGI("Error play sample :%s \n", pcm_get_error(pcm_play));
//play 和 record都是同一个声卡，必须同时关闭
#if 0 // pcm_play 读写失败的情况,重新打开
			pcm_close(pcm_play);//这里close在open pcm声卡是否对pcm_record有影响？？,fix --->pcm_play的关闭打开不影响pcm_record, 但是pcm_record关闭的同时，不关闭pcm_play,重新打开失败
            //所以关闭pcm_play可以不关闭pcm_record,再打开pcm_play，关闭pcm_record必须同时关闭pcm_play和pcm_record，再同时打开
			pcm_play = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
			if(!pcm_play || !pcm_is_ready(pcm_play)){
				//fprintf(stderr,"Unable to open PCM play device (%s)\n",
				//		pcm_get_error(pcm_play));
				LOGI("Unable to open PCM play device (%s)\n",
						pcm_get_error(pcm_play));	
				return ret;//写pcm声卡报错
				
			}else {
				//printf("send_audio_to_tt err to open ok\n");
				LOGI("send_audio_to_tt err to open ok\n");
			}
#else
			return ret;//返回应用层处理
#endif			
		}else{
			to_write -= write_sample;
			tmp += write_sample;
		}
	}
    LOGI(" send_audio_to_tt end \n");
	return 0; //所有pcm数据写完
}

int recieve_audio_byte(JNIEnv *env, jbyteArray audio_frame, jint size)
{
    int len,ret;
    char *buf;
    int i;

    if(!pcm_record || !pcm_is_ready(pcm_record)){
        LOGD("recieve_audio_byte err card not avail \n");
        return -1;
    }

    len = size;
    buf = malloc(len);
    if(!buf){
        LOGI("Unable to allocate %d bytes\n",len);
        free(buf);
        return -1;
    }
    ret = pcm_mmap_read(pcm_record, buf, len);
    if(!ret){
        LOGD("recieve_audio_byte ok\n");
//test ++
        for(i=0; i < 256; i++){
            buf[i] = i;
        }
        for(i=0; i < len; i++){
            LOGD("recieve_audio_byte ==jni==buf[%d]=0x%x\n",i,buf[i]);
        }
//test --
        (*env)->SetByteArrayRegion(env,audio_frame, 0, len, (jbyte *)buf);
        if((*env)->ExceptionCheck(env)){
            LOGD("recieve_audio_byte SetByteArrayRegion err\n");
            (*env)->ExceptionClear(env);
            goto err;
        }
    }else{
        LOGI("recieve_audio_byte Error record sample :%s \n", pcm_get_error(pcm_record));
        goto err;
    }

    LOGD("recieve_audio_byte ok \n");
    free(buf);
    return 0;
    err:
    free(buf);
    return -1;

}

/*
获取天通模块的pcm数据,发送给上位机
返回0:pcm读取正常
返回非0：异常
 数据流天通语音 ----> android lc1860 ---> 上位机pc
*/
static int recieve_tt_audio_to_pc(char *buf, int size){
	int ret;
    int i;

    if(!pcm_record || !pcm_is_ready(pcm_record)){
        LOGD("recieve_tt_audio_to_pc err card not avail \n");
        return -1;
    }
    //这里要判断是否完成了声卡初始化就绪,否则返回错误
	if(size < recieve_from_tt_size){//buf size 应该与recieve_from_tt_size一致
		//fprintf(stderr,"buf size:%d smaller than recieve_from_tt_size:%d \n", size, recieve_from_tt_size );
		LOGI("buf size:%d smaller than recieve_from_tt_size:%d\n", size, recieve_from_tt_size );
	}
	
	if(!recieve_from_tt_buffer){
		recieve_from_tt_buffer = malloc(recieve_from_tt_size);
		if(!recieve_from_tt_buffer){
			//fprintf(stderr,"Unable to allocate %d bytes\n",recieve_from_tt_size);
			LOGI("Unable to allocate %d bytes\n",recieve_from_tt_size);
			free(recieve_from_tt_buffer);
			//pcm_close(pcm_record);
			return -1;
		}
	}
	ret = pcm_mmap_read(pcm_record, recieve_from_tt_buffer, recieve_from_tt_size);//从声卡获取pcm数据
	if(!ret){
		//java_inflect_pcm(recieve_from_tt_buffer, recieve_from_tt_size);//将获取的pcm数据和大小传给java应用处理
		//memcpy(buf, recieve_from_tt_buffer, recieve_from_tt_size);
		//fwrite(recieve_from_tt_buffer, 1, recieve_from_tt_size, file);
		//sendto(recieve_from_tt_buffer, recieve_from_tt_size);//网络通过发送
        //test ++
        for(i=0; i < 256; i++){
            recieve_from_tt_buffer[i] = i;
        }
        for(i=0; i < recieve_from_tt_size; i++){
            LOGD("recieve_from_tt_buffer[%d]=0x%x\n",i,recieve_from_tt_buffer[i]);
        }
        //test --

	}else{//读取pcm报错,重新打开pcm
		//fprintf(stderr,"Error record sample :%s \n", pcm_get_error(pcm_record));
		LOGI("Error record sample :%s \n", pcm_get_error(pcm_record));
#if 0
		printf("recieve_tt_audio_to_pc to pcm_close start \n");
		pcm_close(pcm_record); //在 pcm_play 打开的情况,单独close pcm_record,再打开pcm_record 系统阻塞卡死
		printf("recieve_tt_audio_to_pc to pcm_record start \n");
		//pcm_close(pcm_play);//add test pcm_play pcm_record需要同时关闭，否则有问题,pcm_record出问题需要pcm_play和pcm_record都关闭重启
        pcm_play = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
		pcm_record = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
		printf("recieve_tt_audio_to_pc to pcm_record end \n");
		if(!pcm_record || !pcm_is_ready(pcm_record)){
				fprintf(stderr,"Unable to open PCM record device (%s)\n",
						pcm_get_error(pcm_record));
				LOGI("Unable to open PCM record device (%s)\n",
						pcm_get_error(pcm_record));	
				
				return ret;//读pcm声卡报错
		}else {
			printf(" recieve_tt_audio_to_pc err to  pcm_open ok \n");
			LOGI(" recieve_tt_audio_to_pc err to  pcm_open ok \n");
		}	
#else
		return ret;

#endif
	}

	return ret;
}

int jni_recieve_tt_audio_to_pc(JNIEnv *env_java,jclass clazz, jmethodID methodID)
{
    int ret,status;
    JNIEnv *env = env_java;
   // jclass clazz = NULL;
   // jmethodID voice_static_method;
    jbyteArray  voice_to_java = NULL;

    if(!env || !pcm_record || !pcm_is_ready(pcm_record)){
        LOGD("jni_recieve_tt_audio_to_pc err card not avail \n");
        return -1;
    }

    voice_to_java = (*env)->NewByteArray(env,recieve_from_tt_size);
    if(voice_to_java == NULL){
        LOGD("jni_recieve_tt_audio_to_pc NewByteArray voice_to_java error \n");
        return -1;
    }
/*
    clazz = (*env)->FindClass(env,"com/android/test/jnidemo/AudioFrame");
    if(clazz == NULL){
        LOGD("jni_recieve_tt_audio_to_pc  FindClass error \n");
        goto err1;
    }
*/
/*
    voice_static_method = (*env)->GetStaticMethodID(env,clazz,"TtReceiveVoiceByteFromJNI","(I[B)V");
    if(voice_static_method == NULL){
        LOGD("jni_recieve_tt_audio_to_pc  GetStaticMethodID error \n");
        goto err2;
    }
*/
    ret = recieve_tt_audio_to_pc(recieve_from_tt_buffer,recieve_from_tt_size);
    if(!ret){
        (*env)->SetByteArrayRegion(env, voice_to_java, 0, recieve_from_tt_size, (jbyte *)recieve_from_tt_buffer);
        if((*env)->ExceptionCheck(env)){
            LOGD("jni_recieve_tt_audio_to_pc SetByteArrayRegion ERR");
            goto err2;
        }
    }else{
        LOGD("jni_recieve_tt_audio_to_pc get pcm data err\n");
    }

    (*env)->CallStaticVoidMethod(env, clazz, methodID, recieve_from_tt_size, voice_to_java);
    if((*env)->ExceptionCheck(env)){
        LOGD("jni_recieve_tt_audio_to_pc CallStaticVoidMethod ERR");
        goto err2;
    }

   // (*env)->DeleteLocalRef(env, clazz);
    if(voice_to_java != NULL){
        LOGD("DeleteLocalRef voice_to_java");
        (*env)->DeleteLocalRef(env, voice_to_java);
    }
    return 0;

    err2:
   // (*env)->DeleteLocalRef(env, clazz);
   // LOGD("DeleteLocalRef clazz");

    err1:
    if(voice_to_java != NULL){
        LOGD("err1 DeleteLocalRef voice_to_java");
        (*env)->DeleteLocalRef(env, voice_to_java);
    }
    return -1;

}

//test
pthread_t tt_audio_play;
pthread_t tt_audio_record;

void *tt_audio_play_thread(void *args){
	FILE *file;
	int more_chunks = 1;
	int num_read;
	struct riff_wave_header riff_wave_header;
    struct chunk_header chunk_header;
    struct chunk_fmt chunk_fmt;
	char *filename = "/data/test.wav";
	
	//2:wav file paly
	file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Unable to open file '%s'\n", filename);
        return NULL;
    }
	
	//读取wav文件头部信息
	fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
	if ((riff_wave_header.riff_id != ID_RIFF) ||
        (riff_wave_header.wave_id != ID_WAVE)) {
        fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
        fclose(file);
        return NULL;
    }
	
	//定位pcm的数据
	do {
        fread(&chunk_header, sizeof(chunk_header), 1, file);

        switch (chunk_header.id) {
        case ID_FMT:
            fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
            /* If the format header is larger, skip the rest */
            if (chunk_header.sz > sizeof(chunk_fmt))
                fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
            break;
        case ID_DATA:
            /* Stop looking for chunks */
            more_chunks = 0;
            break;
        default:
            /* Unknown chunk, skip bytes */
            fseek(file, chunk_header.sz, SEEK_CUR);
        }
    } while (more_chunks);
	
	//3:play wav file
	printf("Playing sample: %u ch, %u hz, %u bit\n", pcm_config_audio.channels , pcm_config_audio.rate, chunk_fmt.bits_per_sample);
	printf("frame size:%d\n",send_to_tt_size);
	
#if 1
	//wav play
	do{
		num_read = fread(send_to_tt_buffer, 1, send_to_tt_size, file);
		if(num_read > 0){
			if(send_audio_to_tt(send_to_tt_buffer, send_to_tt_size)){
				fprintf(stderr, "Error playing sample\n");
				break;
			}
		}
	}while(!close_play && num_read > 0);
#endif
	printf("tt_audio_play_thread over \n");
	return NULL;
}

void *tt_audio_record_thread(void *args)
{
	FILE *file_cap;
	char *filename_cap = "/data/cap.wav";
	struct wav_header header;
	int bytes_read = 0;
	int ret;
	
	file_cap = fopen(filename_cap, "wb");
	if(!file_cap){
		fprintf(stderr, "Unable to creat file '%s'\n", filename_cap );
		return NULL;
	}
	
	//录音文件的头文件构造
	header.riff_id = ID_RIFF;
    header.riff_sz = 0;
    header.riff_fmt = ID_WAVE;
    header.fmt_id = ID_FMT;
    header.fmt_sz = 16;
    header.audio_format = FORMAT_PCM;
    header.num_channels = pcm_config_audio.channels;
    header.sample_rate = pcm_config_audio.rate;
	header.bits_per_sample = pcm_format_to_bits(pcm_config_audio.format);
	header.byte_rate = (header.bits_per_sample / 8)*pcm_config_audio.channels*pcm_config_audio.rate;
	header.block_align = pcm_config_audio.channels * (header.bits_per_sample / 8);
	header.data_id = ID_DATA;
	
	//wav cap 
	fseek(file_cap, sizeof(struct wav_header), SEEK_SET);
	
#if 1
	printf("Capturing sample: %u ch, %u hz, %u bit\n", pcm_config_audio.channels, pcm_config_audio.rate,
           pcm_format_to_bits(pcm_config_audio.format));
		   
	while(capturing && !recieve_tt_audio_to_pc(recieve_from_tt_buffer, recieve_from_tt_size)){
		ret = fwrite(recieve_from_tt_buffer, 1, recieve_from_tt_size, file_cap);
		if(ret != recieve_from_tt_size){
			fprintf(stderr, "Error capturing sample\n");
			break;
		}
		bytes_read += recieve_from_tt_size;
	}
	
	/* write harderr information */
	header.data_sz = bytes_read * header.block_align;
	header.riff_sz = header.data_sz + sizeof(header) - 8;
	fseek(file_cap, 0, SEEK_SET);
	fwrite(&header, sizeof(struct wav_header), 1, file_cap);
	fclose(file_cap);
	
	printf("Capture %d frames\n",pcm_bytes_to_frames(pcm_record, bytes_read));
#endif
	return NULL;
	
}

int main(int argc, char **argv)
{
	//1:audio init 
	if(audio_init()){
		return 0;
	}
		
	if(pthread_create(&tt_audio_play, NULL, tt_audio_play_thread, NULL)){
		printf(" tt_audio_play_thread create error \n");
		LOGI(" tt_audio_play_thread create error \n");
		return -1;
	}
	
#if 1
	if(pthread_create(&tt_audio_record, NULL, tt_audio_record_thread, NULL)){
		printf(" tt_audio_record_thread create error \n");
		LOGI(" tt_audio_record_thread create error \n");
		return -1;
	}
#endif

	/* catch ctrl-c to shutdown cleanly */
    signal(SIGINT, stream_close);
	//signal(SIGINT, sigint_handler);
#if 1
	pthread_join(tt_audio_record, NULL);
	printf("tt_audio_record over \n");
#endif 
	pthread_join(tt_audio_play, NULL);
	printf("tt_audio_play over \n");
	
	audio_stop();
	
	return 0;
}
/*
int calculation_ring_buffer_size(int ms,struct pcm_config *config){
    int size=ms*config->rate*config->channels*2/1000;
    int i=31;
    while(i){
        if((size & (1<<i))!=0)
            break;
        else
            i--;
    }

    if(i<=0)
        return 0;
    else
        return 1<<(i+1);
}
//add stephen ---
*/
